"""
@File    :  multi_dhcp_client_layout.py
@Author  :  LeeSen
@Date    :  2024-08-05
@Version :  Python 3.7.4
"""
import time
from multiprocessing import Queue

from PyQt5.QtCore import QTimer
from PyQt5.QtWidgets import QVBoxLayout, QGridLayout, QHBoxLayout, QLabel, \
    QTextEdit, QPushButton, QComboBox, QLineEdit, QMessageBox, QTableWidget, QTableWidgetItem
from dhcp_sniff_thread import DhcpSniffThread
from multi_dhcp_client_thread import MultiDhcpClientThread
from common_config_param import GlobalParam
from common_function import welcome_init, init_nic, clear_log, clear_table_data, log_format_print, int_num_check
from my_dhcp_client import init_dhcp_clients


class MultiDhcpClientTestLayout(QVBoxLayout):
    def __init__(self):
        super().__init__()

        self.timer = QTimer(self)

        self.message_sharing_queues = Queue()
        self.dhcp_client_dict = {}
        self.dhcp_transaction_id_set = set()
        self.host_mac_address_set = set()
        self.host_mac_to_ip_dict = {}
        self.host_mac_to_timer_dict = {}

        self.thread_dict = {}
        self.nic = ""
        self.client_number = 1

        top_layout = QHBoxLayout()

        config_param_layout = QGridLayout()

        nic_label = QLabel("Network Adapter:")
        self.nic_config_box = QComboBox()

        dhcp_client_num_label = QLabel("DHCP Client Num:[1-1024]")
        self.dhcp_client_num_input = QLineEdit()
        self.dhcp_client_num_input.setText("1")

        self.start_button = QPushButton('Start')
        self.start_button.clicked.connect(self.start_test)
        self.start_button.setEnabled(True)
        self.stop_button = QPushButton('Stop')
        self.stop_button.clicked.connect(self.stop_test)
        self.stop_button.setEnabled(False)

        self.clear_log_button = QPushButton('Clear Log')
        self.clear_log_button.clicked.connect(lambda x: clear_log(self.log_area))

        self.clear_table_button = QPushButton('Clear Table')
        self.clear_table_button.clicked.connect(lambda x: clear_table_data(self.table))

        self.reload_network_adapter_btn = QPushButton("Reload NIC")
        self.reload_network_adapter_btn.clicked.connect(lambda x: init_nic(self.nic_config_box))

        config_param_layout.addWidget(nic_label, 0, 0)
        config_param_layout.addWidget(self.nic_config_box, 0, 1)
        config_param_layout.addWidget(dhcp_client_num_label, 0, 2)
        config_param_layout.addWidget(self.dhcp_client_num_input, 0, 3)
        config_param_layout.addWidget(self.start_button, 0, 4)
        config_param_layout.addWidget(self.stop_button, 0, 5)
        config_param_layout.addWidget(self.clear_log_button, 0, 6)
        config_param_layout.addWidget(self.clear_table_button, 0, 7)
        config_param_layout.addWidget(self.reload_network_adapter_btn, 0, 8)

        top_layout.addLayout(config_param_layout)

        self.addLayout(top_layout)

        self.table_layout = QHBoxLayout()
        self.table = QTableWidget()
        self.table.setAlternatingRowColors(True)
        self.table.setStyleSheet(
            "QTableWidget::item:selected { background-color: lightblue; } QTableWidget::item:alternate { "
            "background-color: lightgray; }")

        self.table.setColumnCount(7)

        self.table.setHorizontalHeaderLabels(["MAC", "IP", "Server IP", "Server MAC", "Lease Time", "State", "Time Remaining"])

        self.table_layout.addWidget(self.table)

        self.addLayout(self.table_layout)

        log_label = QLabel("Real-time Logs")
        self.log_area = QTextEdit()
        self.log_area.setPlaceholderText("Log区域")
        self.log_area.setReadOnly(True)

        self.addWidget(log_label)
        self.addWidget(self.log_area)

        init_nic(self.nic_config_box)

        welcome_init(self.log_area)

        self.dhcp_fsm_init()

    def dhcp_fsm_init(self):
        self.log_area.append(
            "|----------------------------------------------------------------------------------------------------------------------------------------|")
        self.log_area.append(
            "|                                                        Yes_Reply | Send_Decline                                                        |")
        self.log_area.append(
            "|          +-----------------------------------------------------------------------------------------------------------+                 |")
        self.log_area.append(
            "|          |                           DHCP_NACK                                                                       |                 |")
        self.log_area.append(
            "|          +--------------------------------------------------------------------+                                      |                 |")
        self.log_area.append(
            "|          |    TimeOut No Offer                                                |                                      |                 |")
        self.log_area.append(
            "|          <-----------------------------+                                      |                                      |                 |")
        self.log_area.append(
            "|          |                             |                                      |                                      |                 |")
        self.log_area.append(
            "|    +-----v-----+            +----------+--------+                   +---------+---------+                 +----------+--------+        |")
        self.log_area.append(
            "|    |           |  Send      |                   |    Send           |                   |  Send_ARP       |                   |        |")
        self.log_area.append(
            "|    |    Init   +----------->|  SELECTING        +------------------>|    REQUESTING     +---------------->|    WAITING        |        |")
        self.log_area.append(
            "|    |           |  Disscover |                   |    Request        |                   |                 |                   |        |")
        self.log_area.append(
            "|    +----^--^---+            +-------------------+                   +-------------------+                 +----------+--------+        |")
        self.log_area.append(
            "|         |  |                                                                         DHCP_ACK                        |No_ARP_Reply     |")
        self.log_area.append(
            "|         |  |                                                                  +-------------------------------+      |                 |")
        self.log_area.append(
            "|         |  |                                                                  |                               |      |                 |")
        self.log_area.append(
            "|         |  |                +-------------------+                   +---------+---------+                 +---v------v--------+        |")
        self.log_area.append(
            "|         |  | DHCP_NACK      |                   |    T2_TimeOut     |                   |   T1_TimeOut    |                   |        |")
        self.log_area.append(
            "|         |  +----------------+   REBINDING       |<------------------+     RENEWING      |<----------------+    BOUND          |        |")
        self.log_area.append(
            "|         |                   |                   |   Send_Request    |                   |  Send_Request   |                   |        |")
        self.log_area.append(
            "|         |                   +----------+--------+                   +---------+---------+                 +----------^--------+        |")
        self.log_area.append(
            "|         |                              |        DHCP_ACK                      |                                      |                 |")
        self.log_area.append(
            "|         |                              +--------------------------------------+--------------------------------------+                 |")
        self.log_area.append(
            "|         |        DHCP_NACK                                                    |                                                        |")
        self.log_area.append(
            "|         +---------------------------------------------------------------------+                                                        |")
        self.log_area.append(
            "|----------------------------------------------------------------------------------------------------------------------------------------|")

    def update_table_data(self):
        self.table.setRowCount(self.client_number)
        row = 0
        for item_id in self.dhcp_client_dict.keys():
            self.table.setItem(row, 0, QTableWidgetItem(str(self.dhcp_client_dict[item_id].mac)))
            self.table.setItem(row, 1, QTableWidgetItem(str(self.dhcp_client_dict[item_id].ip)))
            self.table.setItem(row, 2, QTableWidgetItem(str(self.dhcp_client_dict[item_id].serve_ip)))
            self.table.setItem(row, 3, QTableWidgetItem(str(self.dhcp_client_dict[item_id].server_mac)))
            self.table.setItem(row, 4, QTableWidgetItem(str(self.dhcp_client_dict[item_id].expired)))
            self.table.setItem(row, 5, QTableWidgetItem(str(self.dhcp_client_dict[item_id].current_state)))
            time_remaining = self.dhcp_client_dict[item_id].expired -\
                             (int(time.time() - self.dhcp_client_dict[item_id].start_time))
            if time_remaining < 0:
                time_remaining = 0
            self.table.setItem(row, 6, QTableWidgetItem(str(time_remaining)))
            row += 1
        self.table.viewport().update()

    def start_test(self):
        if GlobalParam.global_running_flag:
            QMessageBox.warning(self.parentWidget().parentWidget(), "WARNING",
                                "Other programs are running, please stop first")
            return
        if self.config_param_check():
            return
        self.nic = self.nic_config_box.currentText()
        self.client_number = int(self.dhcp_client_num_input.text())

        init_data = init_dhcp_clients(self.client_number)
        self.update_table_data()
        self.dhcp_client_dict = init_data["dhcp_client_dict"]
        self.host_mac_address_set = init_data["host_mac_address_set"]
        self.dhcp_transaction_id_set = init_data["dhcp_transaction_id_set"]
        self.host_mac_to_ip_dict = init_data["host_mac_to_ip_dict"]
        self.host_mac_to_timer_dict = init_data["host_mac_to_timer_dict"]

        dhcp_thread_item = MultiDhcpClientThread(self.nic,
                                                 self.message_sharing_queues,
                                                 self.dhcp_client_dict,
                                                 self.host_mac_to_ip_dict,
                                                 self.host_mac_to_timer_dict,
                                                 self.dhcp_transaction_id_set)
        dhcp_thread_item.pkt_signal.connect(self.update_message_or_data)
        self.thread_dict["main"] = dhcp_thread_item
        dhcp_sniff_thread = DhcpSniffThread(self.nic,
                                            self.message_sharing_queues,
                                            self.dhcp_client_dict,
                                            self.host_mac_to_ip_dict)
        dhcp_sniff_thread.pkt_signal.connect(self.update_message_or_data)
        self.thread_dict["sniff"] = dhcp_sniff_thread
        self.thread_dict["sniff"].start()
        self.thread_dict["main"].start()
        self.start_button.setEnabled(False)
        self.stop_button.setEnabled(True)
        GlobalParam.global_running_flag = True
        self.timer.start(1000)
        self.timer.timeout.connect(self.update_table_data)

    def stop_test(self):
        while self.thread_dict["main"].isRunning():
            self.thread_dict["main"].stop()
        while self.thread_dict["sniff"].isRunning():
            self.thread_dict["sniff"].stop()

        self.thread_dict.pop("main")
        self.thread_dict.pop("sniff")

        self.timer.timeout.disconnect()
        self.timer.stop()
        time.sleep(2)
        self.clear_message_sharing_queues()
        self.start_button.setEnabled(True)
        self.stop_button.setEnabled(False)
        GlobalParam.global_running_flag = False

    def clear_message_sharing_queues(self):
        while not self.message_sharing_queues.empty():
            self.message_sharing_queues.get()

    def config_param_check(self):
        dhcp_client_num = self.dhcp_client_num_input.text()
        if int_num_check(dhcp_client_num) or (int(dhcp_client_num) < 1 or int(dhcp_client_num) > 1024):
            QMessageBox.warning(self.parentWidget().parentWidget(),
                                "Invalid DHCP Client Num Param",
                                "The entered DHCP Client Num Param is invalid.[1-1024]")
        return False

    def update_message_or_data(self, res):
        if res["code"] == "404":
            self.stop_test()
            log_format_print(self.log_area, res["message"])
        elif res["code"] == "200":
            log_format_print(self.log_area, res["message"])
        elif res["code"] == "666":
            log_format_print(self.log_area, res["message"] + res["data"])

